// bslstl_function_rep.h                                              -*-C++-*-
#ifndef INCLUDED_BSLSTL_FUNCTION_REP
#define INCLUDED_BSLSTL_FUNCTION_REP

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide a non-template, common implementation for `bsl::function`.
//
//@CLASSES:
// bslstl::Function_Rep: Representation of a `bsl::function` object
//
//@SEE_ALSO: bslstl_function
//
//@DESCRIPTION: This private, subordinate component to `bslstl_function`
// provides a non-template class, `Function_Rep`, that is the data
// representation of `bsl::function` (see {`bslstl_function`}).  The
// `bsl::function` class template uses `bslstl::Function_Rep` to store the
// callable held by the `function` (its *target*), the allocator, and a pointer
// to the function it uses to indirectly invoke the target (the *invoker*
// function).
//
// The client of this component, `bsl::function`, is a complex class that is
// templated in two ways:
//
// 1. The class itself has a template parameter representing the prototype
//    (argument and return types) of its call operator.  E.g.,
//    type `bsl::function<int(char*)>` has member `int operator()(char*);`.
// 2. Several of its constructors are templated on a callable type and wrap an
//    object of that type.  By using type erasure, the type of the wrapped
//    target is not part of the type of the `bsl::function`.
//
// The `Function_Rep` class takes care of the runtime polymorphism required by
// (2), above.  It stores the target object (which can be of any size), copy-
// or move-constructs it, destroys it, and returns its runtime type, size, and
// address.  Nothing in `Function_Rep` is concerned with the call prototype.
//
// `Function_Rep` is a quasi-value-semantic type: It doesn't provide copy and
// move constructors or assignment operators, but it does have the abstract
// notion of an in-memory value (the target object, if not empty) and it
// provides methods for copying, moving, swapping, and destroying that value.
// There is no ability to provide equality comparison because the wrapped
// object is not required to provide equality comparison operations.
//
// `Function_Rep` has only one constructor, which creates an empty object (one
// with no target) using a specified allocator.  The methods of `Function_Rep`
// are a collection of primitive operations for setting, getting, copy
// constructing, move constructing, or swapping the target object, as well as
// accessing the allocator and invoker function pointer.  The methods to set
// and get the invoker pointer represent it as a generic function pointer (the
// closest we could get to `void *` for function pointers), so it is up to the
// caller to cast the pointer back to a specific function pointer type before
// invoking it.

#include <bslscm_version.h>

#include <bslalg_nothrowmovableutil.h>

#include <bslma_constructionutil.h>
#include <bslma_bslallocator.h>

#include <bslmf_assert.h>
#include <bslmf_decay.h>
#include <bslmf_util.h>    // 'forward(V)'

#include <bsls_assert.h>
#include <bsls_compilerfeatures.h>
#include <bsls_platform.h>
#include <bsls_util.h>     // 'forward<T>(V)'

#include <bslstl_function_smallobjectoptimization.h>

#include <cstddef>
#include <cstdlib>
#include <typeinfo>

namespace BloombergLP {
namespace bslstl {

                        // ==================
                        // class Function_Rep
                        // ==================

/// This is a component-private class.  Do not use.
///
/// This class provides a non-template representation for a `bsl::function`
/// instance.  It handles all of the object-management parts of
/// `bsl::function` that are not specific to the prototype (argument list
/// and return type), e.g., storing, copying, and moving the `function`
/// object, but not invoking the `function` (which requires knowledge of the
/// prototype).  These management methods are run-time polymorphic, and
/// therefore do not require that this class be a template (although several
/// of the member functions are templates).
class Function_Rep {

    // PRIVATE CONSTANTS
#if defined(BSLS_PLATFORM_CMP_SUN) && BSLS_PLATFORM_CMP_VERSION < 0x5130
  public:
    // Not really public: made public to work around a Sun compiler bug.
#endif
    static const std::size_t k_NON_SOO_SMALL_SIZE =
                bslstl::Function_SmallObjectOptimization::k_NON_SOO_SMALL_SIZE;

  private:
    // PRIVATE TYPES

    /// Function manager opcode enumerators.  See documentation for the
    /// `functionManager` function template (below).
    enum ManagerOpCode {

        e_MOVE_CONSTRUCT  ,
        e_COPY_CONSTRUCT  ,
        e_DESTROY         ,
        e_DESTRUCTIVE_MOVE,
        e_GET_SIZE        ,
        e_GET_TARGET      ,
        e_GET_TYPE_ID
    };

    /// This union stores either a pointer or a `size_t`.  It is used as
    /// the return type for the manager function (below).
    union ManagerRet {

      private:
        // DATA
        std::size_t  d_asSize_t;
        void        *d_asPtr_p;

      public:
        // CREATORS

        /// Create a union holding the specified `s` `size_t`.
        ManagerRet(std::size_t s);                                  // IMPLICIT

        /// Create a union holding the specified `p` pointer.
        ManagerRet(void *p);                                        // IMPLICIT

        // ACCESSORS

        /// Return the `size_t` stored in this union.  The behavior is
        /// undefined unless this object was constructed with a `size_t`.
        operator std::size_t() const;

        /// Return the pointer stored in this union.  The behavior is
        /// undefined unless this object was constructed with a `TP *`.
        template <class TP>
        operator TP *() const;
    };

    /// Abbreviation for metafunction used to provide a C++03-compatible
    /// implementation of `std::decay` that treats `bslmf::MovableReference`
    /// as an rvalue reference.
    template <class TYPE>
    struct Decay
    : bsl::decay<typename bslmf::MovableRefUtil::RemoveReference<TYPE>::type> {
    };

    typedef bslstl::Function_SmallObjectOptimization Soo;
    typedef Soo::InplaceBuffer                       InplaceBuffer;

    /// Type aliases for convenience.
    typedef bslma::Allocator                         Allocator;

    // DATA

    /// When wrapping a target object that qualifies for the small-object
    /// optimization (as described in the
    /// `bslstl_function_smallobjectoptimization` component), this buffer
    /// stores the in-place representation of the target; otherwise it
    /// stores a pointer to allocated storage holding the target.
    mutable InplaceBuffer d_objbuf;

    /// Allocator used to supply memory
    bsl::allocator<char>  d_allocator;

    /// Pointer to a specialization of the `functionManager` function
    /// template (below) used to manage the current target, or null for
    /// empty objects.
    ManagerRet          (*d_funcManager_p)(ManagerOpCode  opCode,
                                           Function_Rep  *rep_p,
                                           void          *srcFunc_vp);

    /// Pointer to the function used to invoke the current target, or null
    /// for empty objects.  Note that this pointer is always set *after*
    /// `d_funcManager_p` (see state transition table, below).
    void                (*d_invoker_p)();

    // The table below shows progression of states of the a `Function_Rep` from
    // empty through having a fully constructed target.  The progression goes
    // in the reverse direction when setting the `Function_Rep` back to empty.
    // The "target allocated" state is transient and occurs only while a target
    // is being installed; unless otherwise documented, all member functions
    // assume as a class invariant that an object is not in the "target
    // allocated" state.  This state *can* exist at destruction during
    // exception unwinding and is specifically handled correctly by the
    // destructor.
    //
    // d_funcManager_p d_invoker_p  Rep (d_objbuf) state
    // =============== ===========  ======================================
    // NULL            NULL         Initial (Empty)
    // non-NULL        NULL         Target allocated (transient)
    // non-NULL        non-NULL     Target constructed

    // PRIVATE CLASS METHODS

    /// Apply the specified `opCode` to the objects at the specified `rep`
    /// and `srcVoidPtr` addresses and return a pointer or `size_t` result.
    /// If `srcVoidPtr` is non-null, it should point to a callable object of
    /// type indicated by template parameter `FUNC`.  A pointer to an
    /// instantiation of this function is stored in `d_functionManager_p`
    /// and is used to manage the target object wrapped in a `Function_Rep`.
    /// The `FUNC` parameter must not be wrapped in a
    /// `NothrowMovableWrapper`.  The `INPLACE` parameter should be `true`
    /// if and only if the target is allocated inplace within `*rep`.
    ///
    /// The following describes the behavior of each possible value for
    /// `opCode`.  In this description, *the* *target* refers to the object
    /// wrapped within the representation at `rep`.
    ///
    ///: `e_MOVE_CONSTRUCT`:
    ///:   Move construct the target from the callable object at
    ///:   `srcVoidPtr`.  Return the number of bytes needed to hold the
    ///:   object.  The behavior is undefined unless memory has been
    ///:   allocated for the target.
    ///:
    ///: `e_COPY_CONSTRUCT`:
    ///:   Copy construct the target from the callable object at
    ///:   `srcVoidPtr`.  Return the number of bytes needed to hold the
    ///:   object.  The behavior is undefined unless memory has been
    ///:   allocated for the target.
    ///:
    ///: `e_DESTROY`:
    ///:   Call the destructor for the target object but do not deallocate
    ///:   it.  Return the number of bytes needed to hold the destroyed
    ///:   object.  The behavior is undefined unless `*rep` holds a target
    ///:   of type `FUNC`.
    ///:
    ///: `e_DESTRUCTIVE_MOVE`:
    ///:   Move the object at `srcVoidPtr` to the memory allocated for the
    ///:   target and destroy the object at `srcVoidPtr`.  Return the number
    ///:   of bytes needed to hold the target.  This operation is
    ///:   guaranteed not to throw.  If `FUNC` is bitwise movable, perform
    ///:   this move using `memcpy`; otherwise invoke the move constructor
    ///:   followed by the destructor.  The behavior is undefined unless
    ///:   memory has been allocated for the target.  Note that this
    ///:   operation is never invoked unless `FUNC` has either the
    ///:   `bslmf::BitwiseMoveable` or `bsl::is_nothrow_move_constructible`
    ///:   trait.  If `bsl::is_nothrow_move_constructible<FUNC>` is true but
    ///:   the move constructor throws anyway, the program is likely to
    ///:   terminate.
    ///:
    ///: `e_GET_SIZE`:
    ///:   Return the size of a target object of type `FUNC`, encoded using
    ///:   the rules of the `Soo::SooFuncSize` metafunction (see
    ///:   {`bslstl_function_smallobjectoptimization`}).  The arguments
    ///:   `rep` and `srcVoidPtr` are not used.
    ///:
    ///: `e_GET_TARGET`:
    ///:   If the `srcVoidPtr` argument points to `typeid(FUNC)` return a
    ///:   pointer to the target object; otherwise return a null pointer.
    ///:   The behavior is undefined unless `*rep` holds a target of type
    ///:   `FUNC` and `srcVoidPtr` points to a valid `type_info` object.
    ///:
    ///: `e_GET_TYPE_ID`:
    ///:   Return a pointer to the `type_info` for a target object of type
    ///:   `FUNC`.  The `srcVoidPtr` argument is not used.
    ///
    /// Implementation note: Instantiations of this function implement a
    /// kind of hand-coded virtual-function dispatch.  Internally, a
    /// `Manager` function uses a `switch` statement rather than performing
    /// a virtual-table lookup.  This mechanism was chosen because testing
    /// showed that it saves a significant amount of generated code space
    /// over the C++ virtual-function mechanism, especially when the number
    /// of different instantiations of `bsl::function` is large.
    template <class FUNC, bool INPLACE>
    static ManagerRet functionManager(ManagerOpCode  opCode,
                                      Function_Rep  *rep,
                                      void          *srcVoidPtr);

    // PRIVATE MANIPULATORS

    /// Initialize this object's `d_objbuf` field, allocating enough storage
    /// to hold a target of the specified `sooFuncSize`, which is encoded as
    /// per the `Soo::SooFuncSize` metafunction.  If the function qualifies
    /// for the small-object optimization, then the storage comes from the
    /// small-object buffer in `d_objbuf`; otherwise it is obtained from
    /// `d_allocator` and `d_objbuf.d_object_p` is set to the address of the
    /// allocated block.  The target object is not initialized, nor is
    /// `d_funcManager_p` modified.
    void allocateBuf(std::size_t sooFuncSize);

    /// Copy the specified callable object `func` into this object's target
    /// storage (either in-place within this object's small-object buffer or
    /// out-of-place from the allocator).  The behavior is undefined unless
    /// this object is in the target-allocated state for the `func`; which
    /// is when `d_invoker_p == 0` and `d_funcManager_p != 0`.
    template <class FUNC>
    void constructTarget(FUNC& func);
    template <class FUNC>
    void constructTarget(BSLMF_MOVABLEREF_DEDUCE(const FUNC) func);

    /// Move the specified callable object `func` into this object's target
    /// storage (either in-place within this object's small-object buffer or
    /// out-of-place from the allocator).  The behavior is undefined unless
    /// this object is in the target-allocated state for the `func`; which
    /// is when `d_invoker_p == 0` and `d_funcManager_p != 0`.
    template <class FUNC>
    void constructTarget(BSLMF_MOVABLEREF_DEDUCE(FUNC) func);

    // PRIVATE ACCESSORS

    /// Return the size of the target, encoded as per the `Soo::SooFuncSize`
    /// metafunction, or zero if this `function` is empty.
    std::size_t calcSooFuncSize() const BSLS_KEYWORD_NOEXCEPT;

    // NOT IMPLEMENTED
    Function_Rep(const Function_Rep&);
    Function_Rep& operator=(const Function_Rep&);

  public:
    // TYPES

    /// This class does not conform to any specific interface so is not
    /// allocator-aware in the strict sense.  However, this type does hold
    /// an allocator for its AA client and therefore uses the type name
    /// for the allocator preferred by AA types.
    typedef bsl::allocator<char> allocator_type;

    /// A "generic" function type analogous to the data type `void` (though
    /// without the language support provided by `void`).
    typedef void                 GenericInvoker();

    // CREATORS

    /// Create an empty object using the specified `allocator` to supply
    /// memory.
    explicit Function_Rep(const allocator_type& allocator)
                                                         BSLS_KEYWORD_NOEXCEPT;

    /// Destroy this object and its target object (if any) and deallocate
    /// memory for the target object (if not in-place).  This destructor
    /// is implemented to correctly deallocate a target object that has been
    /// allocated but not constructed (e.g., if an exception is thrown
    /// while constructing the target).
    ~Function_Rep();

    // MANIPULATORS

    /// Copy-initialize this rep from the specified `original` rep.  If an
    /// exception is thrown by the copy, the only valid subsequent operation
    /// on this object is destruction.  The behavior is undefined unless
    /// this object is empty before the call.
    void copyInit(const Function_Rep& original);

    /// Do nothing if the specified `func` is a null pointer, otherwise
    /// allocate storage (either in-place within this object's small-object
    /// buffer or out-of-place from the allocator) to hold a target of
    /// (template parameter) type `FUNC`, forward the `func` to the
    /// constructor of the new target, set `d_funcManager_p` to manage the
    /// new target, and set `d_invoker_p` to the specified `invoker`
    /// address.  The behavior is undefined unless this object is empty on
    /// entry.  Note that `FUNC` will not qualify for the small-object
    /// optimization unless
    /// `bsl::is_nothrow_move_constructible<FUNC>::value` is `true`.
    template <class FUNC>
    void installFunc(BSLS_COMPILERFEATURES_FORWARD_REF(FUNC) func,
                     GenericInvoker                          invoker);

    /// Change this object to be an empty object without changing its
    /// allocator.  Any previous target is destroyed and deallocated.  Note
    /// that value returned by `get_allocator().mechanism()` might change,
    /// but will point to an allocator with the same type managing the same
    /// memory resource.
    void makeEmpty();

    /// Move-initialize this rep from the rep at the specified `from`
    /// address, leaving the latter empty.  If 'this->get_allocator() !=
    /// from->get_allocator()', this function degenerates to a call to
    /// `copyInit(*from)`.  The behavior is undefined unless this rep is
    /// empty before the call.
    void moveInit(Function_Rep *from);

    /// Exchange this object's target object, manager function, and invoker
    /// with those of the specified `other` object.  The behavior is
    /// undefined unless `this->get_allocator() == other->get_allocator()`.
    void swap(Function_Rep& other) BSLS_KEYWORD_NOEXCEPT;

    /// If `typeid(TP) == this->target_type()`, return a pointer offering
    /// modifiable access to this object's target; otherwise return a null
    /// pointer.  Note that this function is `const` but returns a
    /// non-`const` pointer because, according to the C++ Standard,
    /// `function` (and therefore this representation) does not adhere to
    /// logical constness conventions.
    template<class TP> TP* target() const BSLS_KEYWORD_NOEXCEPT;

    /// Return a pointer offering modifiable access to this object's target.
    /// If `INPLACE` is true, then the object is assumed to be allocated
    /// inplace in the small object buffer.  Note that this function is
    /// `const` but returns a non-`const` pointer because this type does not
    /// adhere to logical constness conventions.  The behavior is undefined
    /// unless `typeid(TP) == this->target_type()` and `INPLACE` correctly
    /// identifies whether the target is inplace.
    template<class TP, bool INPLACE> TP* targetRaw() const
                                                         BSLS_KEYWORD_NOEXCEPT;

    // ACCESSORS

    /// Return the allocator used to supply memory for this object.
    allocator_type get_allocator() const BSLS_KEYWORD_NOEXCEPT;

    /// Return a pointer the invoker function set using `installFunc` or a
    /// null pointer if this object is empty.
    GenericInvoker *invoker() const BSLS_KEYWORD_NOEXCEPT;

    /// Return `true` if `invoker()` is a null pointer, indicating that this
    /// object has no target object.
    bool isEmpty() const BSLS_KEYWORD_NOEXCEPT;

    /// The `isInplace` function is public in BDE legacy mode and private
    /// otherwise.
#ifdef BDE_OMIT_INTERNAL_DEPRECATED
  private:
#endif
    /// Return `true` if the target is allocated in place within the
    /// small-object buffer of this object; otherwise return `false`.
    bool isInplace() const BSLS_KEYWORD_NOEXCEPT;

  public:
    /// Return a reference to the `type_info` for the type of the current
    /// target object or `typeid(void)` if this object is empty.  If the
    /// target type is a specialization of `bslalg::NothrowMovableWrapper`,
    /// then the returned `type_info` is for the unwrapped type.
    const std::type_info& target_type() const BSLS_KEYWORD_NOEXCEPT;
};

}  // close package namespace

// ============================================================================
//                TEMPLATE AND INLINE FUNCTION IMPLEMENTATIONS
// ============================================================================

                        // --------------------------------------
                        // class bslstl::Function_Rep::ManagerRet
                        // --------------------------------------

// CREATORS
inline
bslstl::Function_Rep::ManagerRet::ManagerRet(std::size_t s)
    : d_asSize_t(s)
{
}

inline
bslstl::Function_Rep::ManagerRet::ManagerRet(void *p)
    : d_asPtr_p(p)
{
}

// ACCESSORS
inline
bslstl::Function_Rep::ManagerRet::operator std::size_t() const
{
    return d_asSize_t;
}

template <class TP>
inline
bslstl::Function_Rep::ManagerRet::operator TP *() const
{
    return static_cast<TP*>(d_asPtr_p);
}

                        // --------------------------
                        // class bslstl::Function_Rep
                        // --------------------------

// PRIVATE CLASS METHODS
template <class FUNC, bool INPLACE>
bslstl::Function_Rep::ManagerRet
bslstl::Function_Rep::functionManager(ManagerOpCode  opCode,
                                      Function_Rep  *rep,
                                      void          *srcVoidPtr)
{
    using bslma::ConstructionUtil;

    // Assert that 'FUNC' is not wrapped.  It should have been unwrapped before
    // instantiating this template.
    BSLMF_ASSERT(! bslalg::NothrowMovableUtil::IsWrapped<FUNC>::value);

    // If 'FUNC' was allocated inplace despite having a throwing move
    // constructor, then 'INPLACE' will disagree with 'Soo::IsInplaceFunc'.  In
    // this case, use the raw size of 'FUNC' rather than the encoded size from
    // 'Soo'.
    static const std::size_t k_SOO_FUNC_SIZE =
        (INPLACE && ! Soo::IsInplaceFunc<FUNC>::value) ?
        sizeof(FUNC) : Soo::SooFuncSize<FUNC>::value;

    // If a function manager exists, then target must have non-zero size.
    BSLMF_ASSERT(0 != k_SOO_FUNC_SIZE);

    FUNC *target = rep->targetRaw<FUNC, INPLACE>();

    switch (opCode) {

      case e_MOVE_CONSTRUCT: {
        // Move-construct function object.  There is no point to optimizing
        // this operation for trivially movable types.  If the type is
        // trivially moveable, then the 'construct' operation below will do it
        // trivially.
        FUNC& original = *static_cast<FUNC *>(srcVoidPtr);
        ConstructionUtil::construct(target,
                                    rep->d_allocator,
                                    bslmf::MovableRefUtil::move(original));
      } break;

      case e_COPY_CONSTRUCT: {
        // Copy-construct function object.  There is no point to optimizing
        // this operation for bitwise copyable types.  If the type is trivially
        // copyable, then the 'construct' operation below will do it trivially.
        const FUNC& original = *static_cast<FUNC *>(srcVoidPtr);
        ConstructionUtil::construct(target, rep->d_allocator, original);
      } break;

      case e_DESTROY: {
        target->~FUNC();
      } break;

      case e_DESTRUCTIVE_MOVE: {
        FUNC *fromPtr = static_cast<FUNC*>(srcVoidPtr);
        ConstructionUtil::destructiveMove(target, rep->d_allocator, fromPtr);
      } break;

      case e_GET_SIZE: {
        return k_SOO_FUNC_SIZE;                                       // RETURN
      }

      case e_GET_TARGET: {
        std::type_info *expType = static_cast<std::type_info *>(srcVoidPtr);
        if (*expType != typeid(FUNC)) {
            // Wrapped type does not match expected type.
            return static_cast<FUNC*>(0);                             // RETURN
        }
        return target;                                                // RETURN
      }

      case e_GET_TYPE_ID: {
        // 'const_cast' needed for conversion to 'ManagerRet'.
        return const_cast<std::type_info*>(&typeid(FUNC));            // RETURN
      }
    } // end switch

    // Any case that doesn't return something explicitly, returns the size of
    // the target object by default.
    return k_SOO_FUNC_SIZE;
}

// PRIVATE MANIPULATORS
template <class FUNC>
void bslstl::Function_Rep::constructTarget(FUNC& func)
{
    BSLS_ASSERT_SAFE(0 != d_funcManager_p);
    BSLS_ASSERT_SAFE(0 == d_invoker_p);

    typedef typename Decay<FUNC>::type DecayedFunc;
    const DecayedFunc&                 decayedFunc = func;

    typedef bslalg::NothrowMovableUtil                        NMUtil;
    typedef typename NMUtil::UnwrappedType<DecayedFunc>::type UnwrappedFunc;
    const UnwrappedFunc& unwrappedFunc = NMUtil::unwrap(decayedFunc);

    d_funcManager_p(
        e_COPY_CONSTRUCT, this, &const_cast<UnwrappedFunc&>(unwrappedFunc));
}

template <class FUNC>
void bslstl::Function_Rep::constructTarget(BSLMF_MOVABLEREF_DEDUCE(const FUNC)
                                               func)
{
    BSLS_ASSERT_SAFE(0 != d_funcManager_p);
    BSLS_ASSERT_SAFE(0 == d_invoker_p);

    constructTarget(bslmf::MovableRefUtil::access(func));
}

template <class FUNC>
void bslstl::Function_Rep::constructTarget(BSLMF_MOVABLEREF_DEDUCE(FUNC) func)
{
    BSLS_ASSERT_SAFE(0 != d_funcManager_p);
    BSLS_ASSERT_SAFE(0 == d_invoker_p);

    typedef typename Decay<FUNC>::type DecayedFunc;
    DecayedFunc&                       decayedFunc = func;

    typedef bslalg::NothrowMovableUtil                        NMUtil;
    typedef typename NMUtil::UnwrappedType<DecayedFunc>::type UnwrappedFunc;
    UnwrappedFunc& unwrappedFunc = NMUtil::unwrap(decayedFunc);

    d_funcManager_p(e_MOVE_CONSTRUCT, this, &unwrappedFunc);
}

// PRIVATE ACCESSORS
inline
std::size_t bslstl::Function_Rep::calcSooFuncSize() const BSLS_KEYWORD_NOEXCEPT
{
    std::size_t ret = 0;

    if (d_funcManager_p) {
        ret = d_funcManager_p(e_GET_SIZE,
                              const_cast<Function_Rep*>(this), 0);
    }

    return ret;
}

// CREATORS
inline
bslstl::Function_Rep::Function_Rep(const allocator_type& allocator)
                                                          BSLS_KEYWORD_NOEXCEPT
    : d_allocator(allocator)
    , d_funcManager_p(0)
    , d_invoker_p(0)
{
}

// MANIPULATORS
template <class FUNC>
void bslstl::Function_Rep::installFunc(
                               BSLS_COMPILERFEATURES_FORWARD_REF(FUNC) func,
                               GenericInvoker                          invoker)
{
    if (! invoker) {
        // Leave this object in the empty state.
        return;                                                       // RETURN
    }

    typedef typename Decay<FUNC>::type DecayedFunc;

    // If 'FUNC' is wrapped in a 'bslalg::NothrowMovableWrapper', then the SOO
    // size calculation works as though 'FUNC' were nothrow movable.
    static const std::size_t k_SOO_FUNC_SIZE =
        Soo::SooFuncSize<DecayedFunc>::value;
    static const bool k_INPLACE = Soo::IsInplaceFunc<DecayedFunc>::value;

    allocateBuf(k_SOO_FUNC_SIZE);  // Might throw

    // Target rep was successfully allocated, set 'd_funcManager_p'.  If 'FUNC'
    // is wrapped in a 'NothrowMovableWrapper', then unwrap it first, but use
    // 'k_INPLACE' value for the original (potentially-wrapped) 'FUNC' so that
    // the function manager knows whether to expect the object to be inplace or
    // not.
    typedef
        typename bslalg::NothrowMovableUtil::UnwrappedType<DecayedFunc>::type
            UnwrappedFunc;
    d_funcManager_p = &functionManager<UnwrappedFunc, k_INPLACE>;

    // Copy or move the function argument into '*this' object.  Note that this
    // operation might throw.
    constructTarget(BSLS_COMPILERFEATURES_FORWARD(FUNC, func));

    // Exception danger has passed.  Setting the invoker makes the
    // 'Function_Rep' non-empty.
    d_invoker_p = invoker;
}

template <class TP>
inline
TP *bslstl::Function_Rep::target() const BSLS_KEYWORD_NOEXCEPT
{
    if (! d_funcManager_p) {
        return 0;                                                     // RETURN
    }

    const std::type_info& tpTypeInfo = typeid(TP);

    void *ret = d_funcManager_p(e_GET_TARGET,
                                const_cast<Function_Rep *>(this),
                                const_cast<std::type_info*>(&tpTypeInfo));

    // If 'TP' is a function (not pointer-to-function) type, 'ret' will be
    // null, but we still must use a C-style cast to avoid the compiler error
    // produced from converting a data pointer to a function pointer.
    return (TP *) ret;
}

template <class TP, bool INPLACE>
inline
TP *bslstl::Function_Rep::targetRaw() const BSLS_KEYWORD_NOEXCEPT
{
    // If target fits in 'd_objbuf', then it is inplace; otherwise, its
    // heap-allocated address is found in 'd_objbuf.d_object_p'.  There is no
    // need to dispatch using metaprogramming because the compiler will
    // optimize away the compile-time conditional test.
    return static_cast<TP*>(INPLACE ? &d_objbuf : d_objbuf.d_object_p);
}

// ACCESSORS
inline
bslstl::Function_Rep::allocator_type
bslstl::Function_Rep::get_allocator() const BSLS_KEYWORD_NOEXCEPT
{
    return d_allocator;
}

inline
bslstl::Function_Rep::GenericInvoker *
bslstl::Function_Rep::invoker() const BSLS_KEYWORD_NOEXCEPT
{
    return d_invoker_p;
}

inline
bool bslstl::Function_Rep::isEmpty() const BSLS_KEYWORD_NOEXCEPT
{
    return 0 == d_invoker_p;
}

}  // close enterprise namespace

#endif // ! defined(INCLUDED_BSLSTL_FUNCTION_REP)

// ----------------------------------------------------------------------------
// Copyright 2020 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
